package bridge

import (
	"encoding/json"
	"fmt"
	"reflect"
	"strings"

	"gitee.com/tomatomeatman/golang-repository/bricks/model/emity"
	"gitee.com/tomatomeatman/golang-repository/bricks/utils/function/app"
	"gitee.com/tomatomeatman/golang-repository/bricks/utils/function/urlutil"
	"gitee.com/tomatomeatman/golang-repository/bricks/utils/ginutil"
	Log "github.com/cihub/seelog"
)

/**
 * 桥接数据操作结构体
 */
type BridgeDao struct{}

var (
	cloudCenterUrl  = "" //Cloud注册中心地址(分布式时使用)
	cloudCenterUser = "" //Cloud注册中心用户名称(分布式时使用)
	cloudCenterPass = "" //Cloud注册中心用户密码(分布式时使用)
	domainName      = "" //项目所在域名
)

// 初始化
func init() {
	cloudCenterUrl = app.AppUtil{}.ReadConfigKey("CloudCenter", "Site", "").(string)
	cloudCenterUser = app.AppUtil{}.ReadConfigKey("CloudCenter", "User", "").(string)
	cloudCenterPass = app.AppUtil{}.ReadConfigKey("CloudCenter", "Password", "").(string)
	domainName = app.AppUtil{}.ReadConfigKey("CloudCenter", "DomainName", "").(string)
}

/**
 * post请求
 * @param serverName 服务名
 * @param url 请求的相对路径
 * @param parameters 请求参数
 * @param resultType 返回类型
 * @param isResultList 返沪类型是否list
 * @return
 */
func (dao BridgeDao) Post(ctx ginutil.Context, serverName, url string, parameters map[string]interface{}, resultType interface{}) *emity.MsgEmity {
	hearMap := urlutil.UrlUtil{}.GetHeader(ctx) //取请求头参数
	return dao.post(serverName, url, parameters, resultType, hearMap)
}

/**
 * 代理请求post
 * 注意:要求请求方返回的必须是*emity.MsgEmity类型
 * @return
 */
func (dao BridgeDao) ProxyPost(ctx ginutil.Context) interface{} {
	domainName := urlutil.UrlUtil{}.GetParam(ctx, "domainName", "GBaseSystemServer").(string)
	url := string([]rune(ctx.Request.URL.String())[6:])

	params := urlutil.UrlUtil{}.GetParamsAll(ctx, false)
	hearMap := urlutil.UrlUtil{}.GetHeader(ctx) //取请求头参数
	m := dao.post(domainName, url, params, &emity.MsgEmity{}, hearMap)
	if !m.Gsuccess {
		return m
	}

	return m.Gdata
}

/**
 * 代理请求get
 * 注意:要求请求方返回的必须是*emity.MsgEmity类型
 * @return
 */
func (dao BridgeDao) ProxyGet(ctx ginutil.Context) interface{} {
	domainName := urlutil.UrlUtil{}.GetParam(ctx, "domainName", "GBaseSystemServer").(string)
	url := string([]rune(ctx.Request.URL.String())[6:])

	params := urlutil.UrlUtil{}.GetParamsAll(ctx, false)
	hearMap := urlutil.UrlUtil{}.GetHeader(ctx) //取请求头参数
	m := dao.get(domainName, url, params, &emity.MsgEmity{}, hearMap)
	if !m.Gsuccess {
		return m
	}

	return m.Gdata
}

/**
 * GET请求
 * @param serverName 服务名
 * @param url 请求的相对路径
 * @param resultType 返回类型
 * @param isResultList 返沪类型是否list
 * @return
 */
func (dao BridgeDao) Get(ctx ginutil.Context, serverName, url string, parameters map[string]interface{}, resultType interface{}) *emity.MsgEmity {
	hearMap := urlutil.UrlUtil{}.GetHeader(ctx) //取请求头参数
	return dao.get(serverName, url, parameters, resultType, hearMap)
}

/**
 * post请求
 * @param serverName 服务名
 * @param url 请求的相对路径
 * @param parameters 请求参数
 * @param resultType 返回类型
 * @param hearMap 自定义请求头
 * @return
 */
func (dao BridgeDao) post(serverName, url string, parameters map[string]interface{},
	resultType interface{}, hearMap map[string]string) *emity.MsgEmity {
	me := dao.findServerSite(serverName)
	if !me.Gsuccess {
		return me
	}

	params := map[string]interface{}{}
	if (nil != parameters) && (len(parameters) > 0) {
		for key, val := range parameters {
			params[key] = val
		}
	}

	url = me.Gdata.(string) + url
	me = urlutil.HttpUtil{}.DoPost(url, params, hearMap)
	if !me.Gsuccess {
		return me
	}

	if nil == me.Gdata {
		return emity.MsgEmity{}.Err(8001, "调用POST未能获取指定结果")
	}

	if nil == resultType {
		return me
	}

	if "string" == reflect.TypeOf(resultType).String() {
		return me
	}

	str := string(me.Gdata.([]uint8))
	err := json.Unmarshal([]byte(str), resultType)
	if err != nil {
		Log.Error("Json字符串转换异常: ", err)
		return emity.MsgEmity{}.Err(8003, "内容转换发生异常")
	}

	return me.SetData(resultType)
}

/**
 * GET请求
 * @param serverName 服务名
 * @param url 请求的相对路径
 * @param resultType 返回类型
 * @param hearMap 自定义请求头
 * @return
 */
func (dao BridgeDao) get(serverName, url string, parameters map[string]interface{},
	resultType interface{}, hearMap map[string]string) *emity.MsgEmity {
	me := dao.findServerSite(serverName)
	if !me.Gsuccess {
		return me
	}

	//url = strings.ReplaceAll(me.Gdata.(string)+url, "//", "/")
	url = me.Gdata.(string) + url
	me = urlutil.HttpUtil{}.DoGet(url, parameters, hearMap)
	if !me.Gsuccess {
		return me
	}

	if nil == me.Gdata {
		return emity.MsgEmity{}.Err(8001, "调用POST未能获取指定结果")
	}

	if nil == resultType {
		return me
	}

	if "string" == reflect.TypeOf(resultType).String() {
		return me
	}

	err := json.Unmarshal([]byte(me.Gdata.(string)), &resultType)
	if err != nil {
		Log.Error("Json字符串转换异常: %+v\n", err)
		return emity.MsgEmity{}.Err(8002, "内容转换发生异常")
	}

	return me.SetData(resultType)
}

/**
 * 访问注册中心,获取服务地址
 * @param serverName
 * @param cloudCenterUrl
 * @return
 */
func (dao BridgeDao) findServerSite(serverName string) *emity.MsgEmity {
	if "" == cloudCenterUrl {
		return emity.MsgEmity{}.Err(7101, "项目未配置注册中心地址,配置项'cloud.center.site'")
	}

	url := strings.ReplaceAll(fmt.Sprintf("http://%s/find/name?sName=%s&key=%s", cloudCenterUrl, serverName, cloudCenterPass), "http://http://", "http://")
	me := urlutil.HttpUtil{}.DoGet(url, nil, nil)
	if !me.Gsuccess {
		return me
	}

	result := emity.MsgEmity{}
	err := json.Unmarshal([]byte(me.Gdata.(string)), &result)
	if err != nil {
		return emity.MsgEmity{}.Err(7102, "访问注册中心失败,未能获取预期信息")
	}

	if nil == result.Gdata {
		return emity.MsgEmity{}.Err(7102, "访问注册中心失败,未能获取预期信息")
	}

	if !result.Gsuccess {
		if reflect.TypeOf(result.Gdata).String() == "int" {
			result.Gdata = result.Gdata.(int) + 200
			return &result
		}

		return &result
	}

	m := result.Gdata.(map[string]interface{})

	if (nil == m["domainName"]) || ("" == m["domainName"]) { //没有启用域名
		result.Gdata = fmt.Sprintf("http://%s:%s", m["serverIp"], m["serverPort"])
		return &result
	}

	if ("" != m["domainName"]) && (domainName == m["domainName"]) { //启用域名,但域名与本程序一致,则属于同一个服务器或同一个局域网内
		result.Gdata = fmt.Sprintf("http://%s:%s", m["serverIp"], m["serverPort"]) //通过ip和端口访问
		return &result
	}

	if (nil == m["domainPort"]) || ("" == m["domainPort"]) { //启用域名但没有使用端口,则端口为80
		result.Gdata = fmt.Sprintf("http://%s", m["domainName"])
		return &result
	}

	//启用域名且使用指定端口

	return result.SetData(fmt.Sprintf("http://%s:%s", m["domainName"], m["domainPort"]))
}
